package cn.rengy.web.framework.securitycode;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import cn.rengy.lang.ResultEntity;
import cn.rengy.web.framework.id.IdGenerator;

/**
 * 验证码服务
 * @author rengy
 *
 */
@Service
public class SecurityCodeService {
	private static Logger logger = LoggerFactory.getLogger(SecurityCodeService.class);
	@Autowired
	private IdGenerator idGenerator;
	private StringRedisTemplate stringRedisTemplate;
	@Autowired
	public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
		this.stringRedisTemplate = stringRedisTemplate;
	}
	private RedisTemplate<Object, Object> redisTemplate;
	@Autowired
	public void setRedisTemplate(RedisTemplate<Object, Object> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}
	private int codeLength=6;//验证码长度
	private int frequency=60000;//发送频率1分钟
	
	private int expireTime=5;//验证码有效期分钟
	
	private int retryNumber=3;//允许重试次数
	
	
	private String getKey(String signName,String key) {
		return new StringBuilder(signName).append("::").append(key).toString();
	}
	private String getCodeKey(String signName) {
		return new StringBuilder(signName).append("::").append(idGenerator.generateId().toString()).toString();
	}
	public ResultEntity<?> verify(String signName,String key,String securityCode) {
		
		if(StringUtils.isBlank(securityCode)) {
			return ResultEntity.fail("验证码错误");
		}
		String id=getKey( signName, key);
		SecurityCodeEntity securityCodeEntity=(SecurityCodeEntity)redisTemplate.opsForValue().get(id);
		boolean verifyResult=false;//验证结果
		if(securityCodeEntity!=null){
			int wrongNumber=securityCodeEntity.getWrongNumber();//重试次数
			List<String> codeList=securityCodeEntity.getCodeList();
			for(String codeId:codeList) {
				String code=stringRedisTemplate.opsForValue().get(codeId);
				if(securityCode.equals(code)) {
					verifyResult=true;
					break;
				}
			}
			if(verifyResult) {//校验成功
				for(String codeId:codeList) {
					stringRedisTemplate.delete(codeId);
				}
				redisTemplate.delete(id);
				return ResultEntity.ok();
			}else {//校验失败
				if(wrongNumber>=retryNumber){//重试次数过多，删除所有验证码
					for(String codeId:codeList) {
						stringRedisTemplate.delete(codeId);
					}
					redisTemplate.delete(id);
					return ResultEntity.fail("验证码错误次数过多请重新发送");
				}else {//增加失败次数
					securityCodeEntity.setWrongNumber(wrongNumber+1);
					//需要更新有效期
					redisTemplate.opsForValue().set(id, securityCodeEntity, redisTemplate.getExpire(id),TimeUnit.SECONDS);
				}
			}
		}
		return ResultEntity.fail("验证码错误或已过期");
	}
	/**
	 * 删除指定key
	 * @param signName
	 * @param key
	 */
	public void evict(String signName,String key) {
		String id=getKey( signName, key);
		redisTemplate.delete(id);
	}
	public ResultEntity<String> save(String signName,String key){
		
		if(StringUtils.isBlank(key)) {
			return ResultEntity.fail("key不能为空");
		}
		String id=getKey( signName, key);
		SecurityCodeEntity securityCodeEntity=(SecurityCodeEntity)redisTemplate.opsForValue().get(id);
		Long nowTime=System.currentTimeMillis();
		if(securityCodeEntity!=null){
			Long lastSendTime=securityCodeEntity.getLastSendTime();
			if(nowTime-lastSendTime<frequency){//上次发送时间大于1分钟
				return ResultEntity.fail("1分钟只能发一次验证码,请稍后重试");
			}
		}
		//1.验证码
		String code= RandomStringUtils.randomNumeric(codeLength);
		String codeKey=getCodeKey(signName);
		stringRedisTemplate.opsForValue().set(codeKey, code, Duration.ofMinutes(expireTime));
		if(securityCodeEntity==null) {
			List<String> codeList=new ArrayList<String>();
			codeList.add(codeKey);
			securityCodeEntity=new SecurityCodeEntity(nowTime, codeList, 0);
		}else {
			securityCodeEntity.setLastSendTime(nowTime);
			securityCodeEntity.getCodeList().add(codeKey);
		}
		//一个key多次发送验证码时会重置过期时间
		this.redisTemplate.opsForValue().set(id, securityCodeEntity, Duration.ofMinutes(expireTime));
		return ResultEntity.ok(code);
		
	}
	
}
